#include <iostream>
#include <atomic>
#include <mutex>
#include <omp.h>

#include "../../utils/timer.hh"

int constexpr count = 80000;

int sum;
std::mutex m;

void lock_inc(int count)
{
    for (auto i = 0; i < count; i++) {
        std::lock_guard<std::mutex> guard(m);
        sum++;
    }
}

std::atomic_int atomic_sum;

void atomic_inc(int count)
{
    for (auto i = 0; i < count; i++) {
        atomic_sum++;
    }
}

void lock_and_atomic_free_inc(int count)
{
    for (int i = 0; i < count; i++)
        sum++;
}

int main()
{
    int nthreads;
    std::cin >> nthreads;
    omp_set_num_threads(nthreads);

    icesp::timer running_timer;

    // experiment 1, lock inc
    running_timer.start();
    sum = 0;
#pragma omp parallel
    {
        lock_inc(count);
    }
    running_timer.stop();

    std::cout << "lock inc: " << running_timer.elapsed_seconds() << "s\n";

    // experiment 2, lock inc
    running_timer.reset();
    running_timer.start();
    atomic_sum = 0;
#pragma omp parallel
    {
        atomic_inc(count);
    }
    running_timer.stop();

    std::cout << "atomic inc: " << running_timer.elapsed_seconds() << "s\n";


    // experiment 2, lock free atomic free inc
    running_timer.reset();
    running_timer.start();
    sum = 0;
#pragma omp parallel
    {
        lock_and_atomic_free_inc(count);
    }
    running_timer.stop();

    std::cout << "lock and atomic free inc: " << running_timer.elapsed_seconds() << "s\n";
}

